package com.module.boots.mp;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.jasypt.util.text.AES256TextEncryptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

import lombok.extern.slf4j.Slf4j;

/**
 * 数据源配置
 * @author：溪云阁
 * @date：2020年5月10日
 */
@ServletComponentScan
@Configuration
@Slf4j
public class DataSourceConfig {

    @Value("${boots.datasource.druid.url}")
    private String url;

    @Value("${boots.datasource.druid.username}")
    private String username;

    @Value("${boots.datasource.druid.password}")
    private String password;

    @Value("${boots.datasource.druid.initial-size:1}")
    private int initialSize;

    @Value("${boots.datasource.druid.max-active:30}")
    private int maxActive;

    @Value("${boots.datasource.druid.min-idle:3}")
    private int minIdle;

    @Value("${boots.datasource.druid.max-wait:60000}")
    private int maxWait;

    @Value("${boots.datasource.druid.time-between-eviction-runs-millis:60000}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${boots.datasource.druid.min-evictable-idle-time-millis:300000}")
    private int minEvictableIdleTimeMillis;

    @Value("${boots.datasource.druid.validation-query:select 'x'}")
    private String validationQuery;

    @Value("${boots.datasource.druid.test-while-idle:true}")
    private boolean testWhileIdle;

    @Value("${boots.datasource.druid.test-on-borrow:false}")
    private boolean testOnBorrow;

    @Value("${boots.datasource.druid.test-on-return:false}")
    private boolean testOnReturn;

    @Value("${boots.datasource.druid.pool-prepared-statements:true}")
    private boolean poolPreparedStatements;

    @Value("${boots.datasource.druid.max-pool-prepared-statement-per-connection-size:20}")
    private int maxPoolPreparedStatementPerConnectionSize;

    @Value("${boots.datasource.druid.filter-class-names:stat,slf4j}")
    private String filters;

    @Value("${boots.datasource.druid.connection-properties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000}")
    private String connectionProperties = "";

    /**
     * 数据源配置
     * @author 溪云阁
     * @return DataSource
     */
    @Bean(initMethod = "init", destroyMethod = "close")
    @Primary
    public DataSource dataSource() {
        final AES256TextEncryptor encryptor = new AES256TextEncryptor();
        encryptor.setPassword("EbfYkitulv73I2p0mXI50JMXoaxZTKJ7");
        DruidDataSource datasource = null;
        try {
            datasource = new DruidDataSource();
            datasource.setDriverClassName("com.p6spy.engine.spy.P6SpyDriver");
            datasource.setUrl(url);
            datasource.setUsername(username);
            datasource.setPassword(encryptor.decrypt(password));
            // 初始化时建立物理连接的个数。初始化发生在显示调用init方法，或者第一次getConnection时
            datasource.setInitialSize(initialSize);
            // 最小连接池数量
            datasource.setMinIdle(minIdle);
            // 最大连接池数量
            datasource.setMaxActive(maxActive);
            // 配置获取连接等待超时的时间
            datasource.setMaxWait(maxWait);
            // 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
            // 配置一个连接在池中最小生存的时间，单位是毫秒
            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            // 用来检测连接是否有效的sql，要求是一个查询语句，常用select 'x'.
            // 如果validationQuery为null，testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
            datasource.setValidationQuery(validationQuery);
            // 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。
            datasource.setTestWhileIdle(testWhileIdle);
            // 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能
            datasource.setTestOnBorrow(testOnBorrow);
            // 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。
            datasource.setTestOnReturn(testOnReturn);
            // 打开PSCache，并且指定每个连接上PSCache的大小
            datasource.setPoolPreparedStatements(poolPreparedStatements);
            datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
            // 配置监控统计拦截的filters，去掉后监控界面sql无法统计:监控统计用的filter:stat;日志用的filter:log4j;防御sql注入的filter:wall'wall'用于防火墙
            datasource.setFilters(filters);
            // 通过connectProperties属性来打开mergeSql功能；慢SQL记录
            datasource.setConnectionProperties(connectionProperties);
        }
        catch (final SQLException e) {
            log.error("数据初始化中出现问题", e);
        }
        return datasource;
    }

    /**
     * druid拦截及账号配置
     * @author 溪云阁
     * @return ServletRegistrationBean<StatViewServlet>
     */
    @Bean
    public ServletRegistrationBean<StatViewServlet> druidServlet() {
        final ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<>();
        reg.setServlet(new StatViewServlet());
        // 添加映射路径
        reg.addUrlMappings("/druid/*");
        // 白名单
        reg.addInitParameter("allow", "");
        // 添加控制台管理用户
        reg.addInitParameter("loginUsername", "admin");
        reg.addInitParameter("loginPassword", "admin@123");
        reg.addInitParameter("resetEnable", "true");
        reg.addInitParameter("logSlowSql", "true");
        return reg;
    }

    /**
     * 过滤资源配置
     * @author 溪云阁
     * @return FilterRegistrationBean<WebStatFilter>
     */
    @Bean
    public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
        final FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        filterRegistrationBean.addInitParameter("profileEnable", "true");
        filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
        filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
        filterRegistrationBean.addInitParameter("DruidWebStatFilter", "/*");
        return filterRegistrationBean;
    }

}
